# -*- coding: utf-8 -*-

import inspect
import sys
import traceback
from pprint import pprint
from colorama import Fore, Style

from hlm.session import Session

UI_MAJOR_VERSION = 1
UI_MINOR_VERSION = 0
UI_VERSION = str(UI_MAJOR_VERSION) + '.' + str(UI_MINOR_VERSION)


class UIColor:
    GREEN = Fore.GREEN
    BLUE = Fore.BLUE


def ui_print_form(form: list):
    if not isinstance(form, list) or len(form) == 0 or len(form[0]) == 0:
        return
    # Get the column size.
    column_size = [0] * len(form[0])
    # Get the size of each column.
    for row in form:
        if not isinstance(row, list):
            return
        for ii, column in enumerate(row):
            column_size[ii] = max(column_size[ii], len(str(column)))
    # Generate the row splitter.
    row_s = '+{}+'.format('+'.join(('-' * (box_size + 2)) for box_size in column_size))
    # Print the column data.
    for row in form:
        print(row_s)
        print('| {} |'.format(' | '.join(str(data).rjust(column_width) for data, column_width in zip(row, column_size))))
    print(row_s)


def ui_cprint(color: Fore, content: str):
    print(color + content + Style.RESET_ALL)


def __get_session_methods():
    # Get the method names of Session.
    session_methods = inspect.getmembers(Session, predicate=inspect.isfunction)
    # Extract name from methods.
    session_method_names = []
    for name, _ in session_methods:
        # Ignore the invisible methods.
        if name.startswith('__'):
            continue
        session_method_names.append(name)
    # Give back the session method name list.
    return session_method_names


def ui_fetch_command(machine_mode: bool = False):
    if not machine_mode:
        return input('> ')
    return input()


def ui_start_session(top_module: type, shuffle: bool = False,
                     machine_mode: bool = False):
    # Generate the top module type as a session.
    session = Session(top_module_type=top_module, shuffle=shuffle)
    # Start execute the session.
    if not machine_mode:
        print('Command-line Debugger v' + UI_VERSION)
        print('Session Top Module: ' + top_module.__name__)
        print('')
    # Define the quit commands.
    quit_commands = ['exit', 'quit', 'q']
    # Get the session available function names.
    session_commands = __get_session_methods()
    # Loop until read all the commands.
    while True:
        # Fetch a new command from user.
        command = ui_fetch_command()
        # Check whether command is quit.
        if command in quit_commands:
            break
        # Backup the step of the session.
        step_before = session.session_step()
        # Now parse the command.
        spliter_pos = command.find(' ')
        if spliter_pos == -1:
            # That is a single command.
            if command not in session_commands:
                print('Error: unknown command ' + command + '\n')
                continue
            # Get the method.
            method = getattr(session, command)
            # Execute the method.
            try:
                result = method()
            except:
                # Extract the error.
                exc_type, exc_value, exc_traceback = sys.exc_info()
                # Print the type and value first.
                print('During the executing, the following error occurs:')
                print('  ' + exc_type.__name__ + ': ' + str(exc_value))
                traceback.print_tb(exc_traceback, limit=None, file=sys.stdout)
                # Reset the result value.
                result = None
        else:
            # Split all the parameters.
            command_list = command.split(' ')
            # Extract the method name.
            method_name = command_list[0]
            # Check the command
            if method_name not in session_commands:
                print('Error: unknown command ' + command + '\n')
                continue
            del command_list[0]
            # Loop and replace all the value.
            for ii in range(len(command_list)):
                command_list[ii] = eval(command_list[ii])
            # Get the method.
            method = getattr(session, method_name)
            # Execute the method.
            result = method(*command_list)
        # If the result is not None, print it.
        if result is not None:
            print(str(result) + '\n')
        # Check function step after.
        step_after = session.session_step()
        # If the step number change, shows the steps it runs.
        if step_after != step_before:
            print('Run {0} step(s), total {1} step(s).'.format(step_after - step_before, step_after))
